package com.nbicc.news.service.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nbicc.attachments.po.Attachment;
import com.nbicc.common.MyValues;
import com.nbicc.common.ResponseObj;
import com.nbicc.common.utils.ExceptionUtil;
import com.nbicc.common.utils.HtmlUtils;
import com.nbicc.hot.service.INewsVisitService;
import com.nbicc.hot.service.impl.NewsVisitServiceImpl;
import com.nbicc.news.vo.ExtInfoAble;
import com.nbicc.newsattachments.po.NewsAttachments;
import com.nbicc.newsattachments.service.impl.NewsAttachmentsServieImpl;
import com.nbicc.newscat.po.NewsCat;
import com.nbicc.parent.async.AsyncTask;
import com.nbicc.common.sequence.Sequence;
import com.nbicc.news.po.NewsContent;
import com.nbicc.news.po.NewsInfo;
import com.nbicc.news.service.INewsContentService;
import com.nbicc.news.service.INewsInfoService;
import com.nbicc.news.service.INewsService;
import com.nbicc.news.vo.News;
import com.nbicc.newscat.service.INewsCatService;
import com.nbicc.spring.TaskExecutorServiceHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.async.DeferredResult;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;

@Slf4j
@Service("NewsServiceImpl")
public class NewsServiceImpl implements INewsService {

    @Autowired
    private MyValues myValues;
    @Autowired
    private Sequence sequence;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private TaskExecutorServiceHolder taskExecutorServiceHolder;
    @Resource(name = "NewsInfoServiceImpl")
    private INewsInfoService newsInfoService;
    @Resource(name = "NewsContentServiceImpl")
    private INewsContentService newsContentService;
    @Resource(name = "NewsCatServiceImpl")
    private INewsCatService newsCatService;
    @Resource(name = "NewsAttachmentsServieImpl")
    private NewsAttachmentsServieImpl newsAttachmentsService;
    @Resource(name = "NewsVisitServiceImpl")
    private INewsVisitService newsVisitService;
    @Override
    public News selectById(Number id) throws Exception {
        News news = new News();
        NewsInfo newsInfo = newsInfoService.selectById(id);
        if(newsInfo == null){
            throw new Exception("没有此新闻" , new Throwable("没有此新闻"));
        }
        NewsContent newsContent = newsContentService.selectById(id);
        List<Attachment> attachments = newsAttachmentsService.selectAttachmentsByNewsId(id);
        news.setNewsInfo(newsInfo);
        news.setNewsContent(newsContent);
        news.setNewsAttachments(attachments);

        applyVisitCountToNews(news,id);

        return news;
    }

    /**
     * 在新闻中设置访问量
     */
    public void applyVisitCountToNews(ExtInfoAble extInfoAble, Number newsId){
        extInfoAble.getExtInfo().put(NewsVisitServiceImpl.VISIT_COUNT, newsVisitService.getVisitCount(newsId));
    }

    @Transactional
    @Override
    public Integer insert(News news) throws Exception {
        long newNewsId = sequence.nextId();
        news.getNewsInfo().setId(newNewsId);
        news.getNewsInfo().setCreateTime(new Date());
        if(StringUtils.isEmpty(news.getNewsInfo().getDigest())){
            news.getNewsInfo().setDigest(HtmlUtils.bodyTextParse(news.getNewsContent().getContent(),myValues.getDigest()));
        }
        news.getNewsContent().setId(newNewsId);
        newsInfoService.insert(news.getNewsInfo());
        newsContentService.insert(news.getNewsContent());
        news.getNewsCats().stream().forEach(newsCat -> newsCat.setNewsId(newNewsId));
        newsCatService.insertAll(news.getNewsCats());

        List<NewsAttachments> newsAttachmentsList = new ArrayList<>();
        if(news.getNewsAttachments() != null){
            news.getNewsAttachments().stream().forEach(newsAttachments -> {
                NewsAttachments na = new NewsAttachments();
                na.setNewsId(newNewsId);
                na.setAttachmentId(newsAttachments.getId());
                newsAttachmentsList.add(na);
            });
        }
        newsAttachmentsService.insertAll(newsAttachmentsList);
        return 1;
    }

    public DeferredResult<Object> asyncInsert(News news) throws Exception {
        DeferredResult<Object> result = new DeferredResult<>();
        AsyncTask task = () ->{
                insert(news);
                result.setResult(ResponseObj.success().setResult(news));
        };
        taskExecutorServiceHolder.submit(task , result);
        return result;
    }

    @Transactional
    @Override
    public Integer updateById(Object o) throws Exception {
//        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); //忽略json字符串中多余的字段
        String jsonString = objectMapper.writeValueAsString(o);
        NewsInfo newsInfo = objectMapper.readValue(jsonString, NewsInfo.class);             //解析新闻描述
        NewsContent newsContent = objectMapper.readValue(jsonString, NewsContent.class);       //解析新闻内容
        if(!StringUtils.isEmpty(newsContent.getContent()) && StringUtils.isEmpty(newsInfo.getDigest())){
            newsInfo.setDigest(HtmlUtils.bodyTextParse(newsContent.getContent(),myValues.getDigest()));
        }
//        List<Attachment> attachments =
//                (ArrayList<Attachment>) ((Map<String, Object>)o ).get("attachments"); //解析新闻附件信息
        List<Attachment> attachments = null;
        JsonNode node=objectMapper.readTree(jsonString);        //生成json对象
        JsonNode data=node.findPath(News.FILED_NEWS_ATTACHMENTS);         //从对象中获取子对象
        if(data != null && data.isArray()){
            attachments= objectMapper.readValue(data.toString(),new TypeReference<List<Attachment>>(){});//把json转换为Java list
        }

        newsInfoService.updateById(newsInfo);
        newsContentService.updateById(newsContent);
        newsAttachmentsService.incrementalModification(newsInfo.getId() , attachments);
        return 1;
    }

    public DeferredResult<Object> asyncUpdateById(Object o) throws Exception {
        DeferredResult<Object> result = new DeferredResult<>();
        AsyncTask task = () ->{
            updateById(o);
        };
        taskExecutorServiceHolder.submit(task , result);
        return result;
    }

    @Override
    public Integer deleteById(Number id) {
        deleteNewsMetadataById(id);
        /**
         * 因为新闻内容是独立删除    所以必须放在最后执行！！！     如果前面操作错误则不会被执行
         */
        newsContentService.deleteById(id);
        return 1;
    }

    /**
     * 元数据删除必须保证原子性
     * @param id
     */
    @Transactional
    public void deleteNewsMetadataById(Number id){
        newsInfoService.deleteById(id);
        Map<String , Object> params = new HashMap<>();
        params.put(NewsCat.FIELD_NEWS_ID, id);
        newsCatService.deleteByMap(params);          //删除新闻和目录关系
        newsVisitService.deleteVisitCount(id);
        try {
            newsAttachmentsService.deleteByNewsId(id);  //删除新闻和附件关系（如果失败 不回滚 影响使用）
        } catch (IOException e) {
            e.printStackTrace();
            log.error(ExceptionUtil.getStackTraceInfo(e));
        }
    }

}
